package tablefilter;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.input.MouseEvent;

public class ColumnFilter<S, T> {
	
	public static final int COLUMN_TYPE_DEFAULT = 0 ; 
	public static final int COLUMN_TYPE_CALCULATE = 1 ;
	public static final int COLUMN_TYPE_REGULAR = 2 ; 
	
	private TableFilter<S> tableFilter;
	private TableColumn<S, T> column;
	private FilteredColumnHeader header;
	private FilterPopUp<T> filterPopup;
	private ListProperty<T> mergedCellValues = (ListProperty<T>) new SimpleListProperty<>(FXCollections.observableArrayList());
	private ListProperty<T> blacklistedCellValues = (ListProperty<T>) new SimpleListProperty<>(FXCollections.observableArrayList());
	ObservableList<Integer> observableArrayListTemp = FXCollections.observableArrayList();
	private ListProperty<Integer> blacklistedRowIndexes = new SimpleListProperty<>(observableArrayListTemp);
	private Map<T, List<Integer>> mapCellValuesToRowIndexes = new HashMap<>();
	private Comparator<T> comparator = new Comparator<T>() {
		@Override
		public int compare(T o1, T o2) {
			return String.valueOf(o2).compareTo(String.valueOf(o1));
		}
		
	};
	public ColumnFilter(TableFilter<S> tablefilter, TableColumn<S, T> columnToFilter , int columnType){
		super();
		this.tableFilter = tablefilter ; 
		this.column = columnToFilter ; 
		final EventHandler<CellEditEvent<S, T>> editCommitHandler = this.column.getOnEditCommit();
		this.column.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<S,T>>() {
			@Override
			public void handle(CellEditEvent<S, T> event) {
				editCommitHandler.handle(event);
				updateCellValues();
			}
		});
		header = new FilteredColumnHeader(column);
		header.prefWidthProperty().bind(column.widthProperty());
		//注册按钮事件
		header.getFilterButton().setOnMouseClicked(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent event) {
				onFilterButtonClicked(event);
			}
		});
		header.isFilteredProperty().bind(blacklistedCellValues.emptyProperty().not());
		column.setGraphic(header);
		filterPopup = new FilterPopUp<>(mergedCellValues, blacklistedCellValues , columnType);
		blacklistedCellValues.addListener(new ListChangeListener<T>(){
			@Override
			public void onChanged(javafx.collections.ListChangeListener.Change<? extends T> changeEvent) {
				onBlacklistedCellValuesChanged(changeEvent);
			}
		});
		initColumnFilter();
	}
	
	private void initColumnFilter() {
		filterPopup.getRootContent().maxHeightProperty().bind(tableFilter.getTableView().heightProperty().multiply(0.7));
		updateCellValues();
		tableFilter.unfilteredItemsProperty().addListener(new ListChangeListener<S>(){
			@Override
			public void onChanged(javafx.collections.ListChangeListener.Change<? extends S> c) {
				blacklistedCellValues.removeListener(new ListChangeListener<T>(){
					@Override
					public void onChanged(javafx.collections.ListChangeListener.Change<? extends T> changeEvent) {
						onBlacklistedCellValuesChanged(changeEvent);
					}
					
				});
				blacklistedCellValues.clear();
				blacklistedRowIndexes.clear();
				updateCellValues();
				blacklistedCellValues.addListener(new ListChangeListener<T>(){
					@Override
					public void onChanged(javafx.collections.ListChangeListener.Change<? extends T> changeEvent) {
						onBlacklistedCellValuesChanged(changeEvent);
					}
					
				});
			}
		});
	}
	
	
	private void onFilterButtonClicked(MouseEvent event) {
		if (!filterPopup.isShowing()) {
			filterPopup.show(header, event.getScreenX(), event.getScreenY() + 20);
			
		}
		event.consume();
	}
	
	private void onBlacklistedCellValuesChanged(Change<? extends T> changeEvent) {
		List<Integer> addedIndexes = new ArrayList<>();
		List<Integer> removedIndexes = new ArrayList<>();
		while (changeEvent.next()) {
			for (T addedItem : changeEvent.getAddedSubList()) {
				addedIndexes.addAll(mapCellValuesToRowIndexes.get(addedItem));
			}
			for (T removedItem : changeEvent.getRemoved()) {
				removedIndexes.addAll(mapCellValuesToRowIndexes.get(removedItem));
			}
		}
		blacklistedRowIndexes.addAll(addedIndexes);
		blacklistedRowIndexes.removeAll(removedIndexes);
	}
	
	private void updateCellValues() {
		mapIndexesToCellValues();
		updateMergedCellValues();
	}	
	
	private void updateMergedCellValues() {
		Set<T> mergedValues = new TreeSet<>(comparator);
		for (T value : mapCellValuesToRowIndexes.keySet()) {
//			if(StringUtils.contains(String.valueOf(value), "<") && StringUtils.contains(String.valueOf(value), ">")){
//				mergedValues.add(null);
//			}else{
//				mergedValues.add(value);
//			}
			mergedValues.add(value);
		}
		mergedCellValues.setAll(mergedValues);
	}
	
	private void mapIndexesToCellValues() {
		mapCellValuesToRowIndexes.clear();
		for (int i = 0; i < tableFilter.getUnfilteredItems().size(); i++) {
			S item = tableFilter.getUnfilteredItems().get(i);
			T cellValue = column.getCellValueFactory().call(new CellDataFeatures<>(tableFilter.getTableView(), column, item)).getValue();	
			List<Integer> indexes = mapCellValuesToRowIndexes.get(cellValue);
			if (indexes == null) {
				indexes = new ArrayList<>();
				mapCellValuesToRowIndexes.put(cellValue, indexes);
			}
			indexes.add(i);
		}
	}
	
	public ListProperty<Integer> blacklistedRowIndexesProperty() {
		return this.blacklistedRowIndexes;
	}
	public final ObservableList<Integer> getBlacklistedRowIndexes() {
		return this.blacklistedRowIndexes.get();
	}

	
}
